package thread;
//1.线程,用于扫描任务
//2.优先级队列,保存所有的任务
import java.util.PriorityQueue;

//4.创建一个类,来描述任务
class MyTimerTask implements Comparable<MyTimerTask>{
    private long time;//什么时间点来执行这个任务,此处规定时间是一个ms级别的时间戳
    private Runnable runnable;//实际任务要执行的代码
    //delay是一个相对时间,以注册任务的时间为基准
    public MyTimerTask(Runnable runnable,long delay) {
        this.time = delay+System.currentTimeMillis();//计算真正要执行任务的绝对时间
        this.runnable = runnable;
    }

    public long getTime() {
        return time;
    }

    public void run(){
        runnable.run();
    }

    @Override
    public int compareTo(MyTimerTask o) {
        return (int)(this.time-o.time);
    }

}
//1.通过这个类,来表示我们自己编写的定时器
class MyTimer{
    //2.负责扫描队列并且执行任务的线程
    private Thread t = null;
    //3.优先级队列,用于保存任务
    private PriorityQueue<MyTimerTask> queue = new PriorityQueue<MyTimerTask>();//元素要可比较大小
    //6.创建锁对象
    Object locker = new Object();
    public void schedule(Runnable runnable,long delay){
        synchronized (locker){
            MyTimerTask task = new MyTimerTask(runnable,delay);
            queue.offer(task);
            //添加新的元素之后,就可以唤醒扫描线程的wait()了
            locker.notify();
        }
    }
    //9.提供cancel方法,用于结束t线程
    public void cancel(){
        //通过interrupt方法
    }
    //5.创建构造方法,用于创建扫描线程,让扫描线程完成判定和执行任务的操作
    public MyTimer(){
        t=new Thread(()->{
            //扫描线程反复扫描队首元素,如果时间到了,就执行任务并出队列,如果时间没到就等待
            while(true){
                try {
                    //锁不能加在这里,会一直专用锁
                    synchronized (locker){//锁用于保护队列元素
                        while(queue.isEmpty()){
                            //7.如果当前这里为空,那么继续循环没有意义
                            locker.wait();
                        }
                        MyTimerTask task = queue.peek();
                        long cur = System.currentTimeMillis();
                        if(cur>=task.getTime()){
                            //如果当前时间已经达到执行时间,那么任务就可以执行了
                            queue.poll();
                            task.run();
                        }else {
                            //8.当前时间还没到,暂时先不执行
                            //此处不能使用sleep(),一方面会错过新的任务,另一方面也无法释放锁
                            //wait过程中,有新的任务来了,schedule方法中的notify会将这里唤醒,每来一个新的任务,都要重新取队首元素判断一遍
                            //没有新的任务,按既定时间wait即可
                            locker.wait(task.getTime()-cur);
                        }
                    }//此处解锁
                }catch (InterruptedException e){
                    e.printStackTrace();
                }

            }

        });
        //要记得start
        t.start();
    }
}
public class ThreadDemo6 {
    public static void main(String[] args) {
        MyTimer myTimer = new MyTimer();

        //在main线程中对元素进行添加
        //另外一个线程(扫描线程)在对队列进行删除
        myTimer.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("GEE GEE GEE");
                }
            },1000);
        myTimer.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("BABY BABY BABY");
            }
        },2000);
        System.out.println("hello main");



    }
}
